package com.test.A26;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.ModelFactory;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver;

import com.test.A26.WebConfig.Controller1;
import com.test.A26.WebConfig.User;


/***
 * ModelAttribute测试
 *
 */
public class A26 {

    public static void main(String[] args) throws Exception {
    	test2();
    }
    /****
     * handlerMethod 简单执行流程 
     * 模拟请求 ，然后在方法中参数中得到请求的参数值  得到方法注解上的@ModelAttribute
     *  
     */
    public static void test2() throws Exception{
   	 AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(WebConfig.class);

        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("name", "张三");//创建模拟请求对象，并且设置值
        // 现在可以通过 ServletInvocableHandlerMethod 把这些整合在一起, 并完成控制器方法的调用, 如下 设置bean和method
        ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(new Controller1(), Controller1.class.getMethod("foo", User.class));

        RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter(); //创建HandlerAdapter
        adapter.setApplicationContext(context);
        adapter.afterPropertiesSet();
        
        ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
        //创建数据绑定工厂  不需要设置绑定方法和初始化器
        handlerMethod.setDataBinderFactory(factory); //把数据绑定工厂 设置到ServletInvocableHandlerMethod
        handlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());//把设置参数名解析器绑定到ServletInvocableHandlerMethod
        handlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers(context));//把参数解析器添加ServletInvocableHandlerMethod
        //创建ModelAndViewContainer
        ModelAndViewContainer container = new ModelAndViewContainer();
        
        
        // 获取模型工厂方法
        Method getModelFactory = RequestMappingHandlerAdapter.class.getDeclaredMethod("getModelFactory", HandlerMethod.class, WebDataBinderFactory.class);
        getModelFactory.setAccessible(true);
        //创建模型工厂
        ModelFactory modelFactory = (ModelFactory) getModelFactory.invoke(adapter, handlerMethod, factory);
        
        // 初始化模型数据
        modelFactory.initModel(new ServletWebRequest(request), container, handlerMethod);
        
        //调用
        handlerMethod.invokeAndHandle(new ServletWebRequest(request), container);
        
        System.out.println(container.getModel());//从model中得到缓存的值
        context.close();
   }
    
    
    /****
     * handlerMethod 简单执行流程 
     * 模拟请求 ，然后在方法中参数中得到请求的参数值  得到方法参数上的@ModelAttribute
     *  
     */
    public static void test1() throws Exception{
    	 AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(WebConfig.class);

         MockHttpServletRequest request = new MockHttpServletRequest();
         request.setParameter("name", "张三");//创建模拟请求对象，并且设置值
         // 现在可以通过 ServletInvocableHandlerMethod 把这些整合在一起, 并完成控制器方法的调用, 如下 设置bean和method
         ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(new Controller1(), Controller1.class.getMethod("foo", User.class));

         ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
         //创建数据绑定工厂  不需要设置绑定方法和初始化器
         handlerMethod.setDataBinderFactory(factory); //把数据绑定工厂 设置到ServletInvocableHandlerMethod
         handlerMethod.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());//把设置参数名解析器绑定到ServletInvocableHandlerMethod
         handlerMethod.setHandlerMethodArgumentResolvers(getArgumentResolvers(context));//把参数解析器添加ServletInvocableHandlerMethod
         //创建ModelAndViewContainer
         ModelAndViewContainer container = new ModelAndViewContainer();
         //调用
         handlerMethod.invokeAndHandle(new ServletWebRequest(request), container);
         
         System.out.println(container.getModel());//从model中得到缓存的值
         context.close();
    }

    /****
     * 添加参数解析器组合
     * @return
     */
    public static HandlerMethodArgumentResolverComposite getArgumentResolvers(AnnotationConfigApplicationContext context) {
        HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
        composite.addResolvers(
                new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), false),
                new PathVariableMethodArgumentResolver(),
                new RequestHeaderMethodArgumentResolver(context.getDefaultListableBeanFactory()),
                new ServletCookieValueMethodArgumentResolver(context.getDefaultListableBeanFactory()),
                new ExpressionValueMethodArgumentResolver(context.getDefaultListableBeanFactory()),
                new ServletRequestMethodArgumentResolver(),
                new ServletModelAttributeMethodProcessor(false),
                new RequestResponseBodyMethodProcessor(Arrays.asList(new MappingJackson2HttpMessageConverter())),
                new ServletModelAttributeMethodProcessor(true),
                new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), true)
        );
        return composite;
    }



}
